package com.zhike.service.im;

import java.io.IOException;
import java.io.InputStream;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.apache.zookeeper.ZKUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.zhike.mobileimsdk.server.ServerLauncher;
import com.zhike.mobileimsdk.server.event.MessageQoSEventListenerS2C;
import com.zhike.mobileimsdk.server.event.ServerEventListener;
import com.zhike.mobileimsdk.server.network.Gateway;
import com.zhike.mobileimsdk.server.network.GatewayTCP;
import com.zhike.mobileimsdk.server.network.GatewayUDP;
import com.zhike.mobileimsdk.server.network.GatewayWebsocket;
import com.zhike.mobileimsdk.server.processor.BridgeProcessor;
import com.zhike.mobileimsdk.server.qos.QoS4ReciveDaemonC2S;
import com.zhike.mobileimsdk.server.qos.QoS4SendDaemonS2C;
import com.zhike.mobileimsdk.server.utils.RedisIns;
import com.zhike.mobileimsdk.server.utils.ServerToolKits;
import com.zhike.mobileimsdk.server.utils.ZkUtil;
import com.zhike.mobileimsdk.server.utils.ServerToolKits.SenseModeTCP;
import com.zhike.common.conf.IMBaseConf;
import com.zhike.common.utils.RedisService;

import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import lombok.extern.slf4j.Slf4j;

/**
 * IM服务启动入口类。
 * 
 * @version 1.0
 */
@Slf4j
@Component
public class ChatServerLauncher extends ServerLauncher {

  @Resource
  private IMBaseConf baseConf;

  @Resource
  private MessageQoSEventListener messageQoSEventListner;

  @Resource
  private ChatServerEventListener chatServerEventListener;

  @Autowired
  private RedisService redisService;

  @PostConstruct
  public void initial() throws IOException {
    nodeName = baseConf.getNODE_NAME();
    // 设置UDP网络监听端口（如果需修改此端口号，请前往配置文件中修改）
    GatewayUDP.PORT = baseConf.getIM_SERVER_UDP_LISTENNING_PORT();
    // 设置TCP网络监听端口（如果需修改此端口号，请前往配置文件中修改）
    GatewayTCP.PORT = baseConf.getIM_SERVER_TCP_LISTENNING_PORT();
    // 设置Websocket网络监听端口（如果需修改此端口号，请前往配置文件中修改）
    GatewayWebsocket.PORT = baseConf.getIM_SERVER_WEBSOCKET_LISENNING_PORT();

    // 设置服务端支持UDP、TCP协议情况（默认同时支持两种，如需修改请前往配置文件中修改）
    boolean isSupportUDP = baseConf.getIM_SERVER_UDP_SUPPORT();
    boolean isSupportTCP = baseConf.getIM_SERVER_TCP_SUPPORT();
    boolean isSupportWebSocket = baseConf.getIM_SERVER_WEBSOCKET_SUPPORT();
    ServerLauncher.supportedGateways = (isSupportUDP ? Gateway.SOCKET_TYPE_UDP : 0)
        | (isSupportTCP ? Gateway.SOCKET_TYPE_TCP : 0)
        | (isSupportWebSocket ? Gateway.SOCKET_TYPE_WEBSOCKET : 0);

    // 开启或关闭Debug日志输出
    QoS4SendDaemonS2C.getInstance().setDebugable(baseConf.getIM_SEND_DEBUG_ENABLED());
    QoS4ReciveDaemonC2S.getInstance().setDebugable(baseConf.getIM_RECIVE_DEBUG_ENABLED());

    // 与客户端协商一致的心跳频率模式设置
    ServerToolKits.setSenseModeTCP(SenseModeTCP.MODE_5S);

    // 设置最大TCP帧内容长度（不设置则默认最大是 6 * 1024字节）
    GatewayTCP.TCP_FRAME_MAX_BODY_LENGTH = 60 * 1024;

    // 开启TCP协议的SSL/TLS加密传输（请确保客户端也已开发SSL）
    // GatewayTCP.sslContext = createSslContext();

    // 关闭与Web端的消息互通桥接器
    ServerLauncher.bridgeEnabled = baseConf.getIM_BRIDGE_ENABLED();
    // 跨服桥接器MQ的URI（本参数只在ServerLauncher.bridgeEnabled为true时有意义）
    BridgeProcessor.IMMQ_URI = baseConf.getIM_BRIDGE_MQ_URI();

    // ZK
    ZkUtil.ZK_ENABLE = baseConf.getIM_SERVER_ZK_SUPPORT();
    ZkUtil.ZK_HOST = baseConf.getIM_SERVER_ZK_HOST();

    // redis
    RedisIns.getInstance().setRedis(redisService);
  }

  public ChatServerLauncher() throws IOException {
    super();
  }

  /**
   * 初始化回调处理事件监听器。
   * <p>
   * 本类重写 {@link #initListeners()}方法，以实现应用层的回调监听器以便实现自定义业务
   * 逻辑，可以设置的回调监听器有： {@link #setServerEventListener(ServerEventListener)}
   * 和 {@link #setServerMessageQoSEventListener(MessageQoSEventListenerS2C)}。
   */
  @Override
  protected void initListeners() {
    // ** 设置回调
    this.setServerEventListener(chatServerEventListener);
    this.setServerMessageQoSEventListener(messageQoSEventListner);
  }

  /**
   * 重写父类的本方法，方便直接使用本工程中的ChatServerEventListener（即ServerEventListener
   * 接口的实现类。
   * 
   * {@inheritDoc}
   */
  @Override
  public ChatServerEventListener getServerEventListener() {
    return (ChatServerEventListener) serverCoreHandler.getServerEventListener();
  }

  /**
   * 创建SslContext对象，用于开启SSL/TLS加密传输。
   *
   * @return 如果成功创建则返回SslContext对象，否则返回null
   */
  private static SslContext createSslContext() {
    try {
      /** 示例 1：使用证书（证书位于绝对路径） */
      // // 证书文件
      // File certChainFile = new File("c:/certs/netty-cert2.crt");
      // // 证书文件
      // File keyFile = new File("c:/certs/netty-key2.pk8");
      // // 私钥密码（注意：Netty只支持.pk8格式，如何生成，见JackJiang文章：）
      // String keyPassword = "123456";
      // // 生成SslContext对象（为了方便理解，此处使用的是单向认证）
      // SslContext sslCtx = SslContextBuilder.forServer(certChainFile, keyFile,
      // keyPassword).clientAuth(ClientAuth.NONE).build();

      /** 示例 2：使用证书（证书位于相对路径） */
      // TODO: 注意：请使用自已的证书，Demo中带的证书为自签名证书且已绑定域名，不安全！！！
      // 证书文件
      InputStream certChainFile = ChatServerLauncher.class.getResourceAsStream("certs/netty-cert2.crt");
      // 私钥文件（注意：Netty只支持.pk8格式，如何生成，见JackJiang文章：）
      InputStream keyFile = ChatServerLauncher.class.getResourceAsStream("certs/netty-key2.pk8");
      // 私钥密码（注意：Netty只支持.pk8格式，如何生成，见JackJiang文章：）
      String keyPassword = "123456";
      // 生成SslContext对象（为了方便理解，此处使用的是单向认证）
      SslContext sslCtx = SslContextBuilder.forServer(certChainFile, keyFile, keyPassword).clientAuth(ClientAuth.NONE)
          .build();

      /** 示例 3：使用Netty自带的自签名证书（建议该证书仅用于测试使用） */
      // SelfSignedCertificate ssc = new SelfSignedCertificate();
      // SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(),
      // ssc.privateKey()).build();

      return sslCtx;
    } catch (Exception e) {
      log.warn("createSslContext()时出错了，原因：" + e.getMessage(), e);
    }

    return null;
  }

}
